{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "自动化Turtle代码生成器"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "软件思路：\n",
    "    turtle作为python自带的，基于tkinter实现窗体显示功能的官方自带库，拥有简单易上手的绘图功能，除了有goto语句这样的直接移动到对应笛卡尔坐标的方式之外，还有forward等使画笔朝当前朝向移动固定距离的方法，和相对应的转向方法，导致turtle画图很像是一辆遥控的小车。\n",
    "    本程序的实现思路是尽可能不使用goto这样的直接到点的行进方式，而是使用最贴合turtle本身的实现方式，“驾驶”画笔进行画图。\n",
    "    传统turtle画图面临的最大难题是对复杂图像的绘制能力，越复杂的图，人工编写代码说需要调试的内容越多，当图像复杂到一定程度，使用turtle绘制图像就已经成为了一个极其困难的工程，假设如果要涉及到照片等信息含量极其巨大且不规则图像时候，turtle的正常编写代码模式就已经无法工作了。\n",
    "    那么有没有什么方法可以直接还原任意复杂的图像呢？\n",
    "    其实是有的，那就是直接写一个生成代码本身的代码，由代码生成器输出绘制对应图像所需的turtle代码。\n",
    "    要输出能够绘制图像的turtle代码，本身这个生成器就得符合turtle对复杂图像自动绘制的原理，目前来讲，在排除更高精尖的计算机视觉领域方面的技术以外，要满足能绘制‘任意’复杂图像，那么就得将工作单位简化到像素(pixel)和像素构成的线条，以及线条围起来的一个区域组成的面。\n",
    "    根据以上思路，可以分解成三个不同的工作原理：\n",
    "\n",
    "    1.对图片所有颜色进行遍历，在当前颜色中，用goto方法在所有为原始图像像素点为该色值的像素坐标，于画板上绘制，当所有颜色都遍历完成后，所有的像素理所当然的都被填充完毕，而由像素构成的图像也绘制完毕。（仅像素原理）\n",
    "\n",
    "    2.采用打印机的逐行打印方式，使用固定方向的forward方法，沿行打印打印成线，该线条上任意像素点的颜色为原始图像对应坐标的颜色，即在线条前进的过程中，不停的变化线条的颜色，留下‘墨迹’，到达行的末尾时进行换行，重复进行以上的前进模式。因为留下的‘墨迹’具有一像素的高度，最终所有行被打印完成后，完整的图像就像被织布机织出来的布一样具备了原始图像的所有色彩信息。(像素组成线条的原理)\n",
    "\n",
    "    3.先分解图像，遍历所有像素，将相同颜色连起来的像素视为一个组(Group)，最终所有像素将被分为不同的组，且根据每个组的像素内容，分别理解为‘点’、‘线’、‘面’。绘制过程中，因像素的排布是矩阵式的，所以将画笔的前进方向约束为八个方向(N,S,E,W,NE,NW,SE,SW)，这样一来所有的前进方向都会存在相邻像素，可作为下一个前进的方向，使用‘绘制逻辑’函数和‘方向选择’函数对不同类型的组采用不同的绘制方法。当绘制‘面’时，只需要将‘面’的外轮廓全部走一遍，然后使用填充方法(fill)就可以填充大量内容，无需单个像素绘制，可节省大量的代码操作，但‘面’的填充具备特殊性，后绘制的‘面’在填充的过程中，可能会因为坐标包含的原因，覆盖掉之前绘制的内容，导致之前绘制的像素信息丢失。未避免此情况出现，需提前计算出所有组的包含关系，建立‘子级’和‘父级’，分层所有组，由包含关系的最底层优先绘制，就不会导致上层内容被覆盖导致信息丢失。\n",
    "\n",
    "    第三种即为本生成器所采用的方法，兼具点，线，面的工作方式，更符合逻辑性，输出的代码也更相对更贴近于人工的实现方式。\n",
    "\n",
    "    而在实际的实现过程中，如果原始图像为照片等超高信息密度图像，会因为自然环境下光的复杂性导致看上去一样的颜色，其RGB色值差别细微，但因细微差别无法合并为组，仅能单独成组，为防止该情况导致的‘面’类型组无法生成，所以采用了降低原始图像色彩复杂的的方式，通过对颜色的相近值合并，使出现‘点’的组大幅减少，增加‘面’和‘线’的数量，为防止颜色合并过量导致的图像差异过大，增加了合并颜色的只有选择，以及增加了合并基准\n",
    "    色色彩偏移的方式，尽量保证人类观感。\n",
    "\n",
    "    当生成器本身可根据该原理进行执行时，其所进行的操作、使用的代码也一定可在其他地方复现，仅需将所有生成过程中对turtle库操作的代码同步输出为字符串，以python文件格式保存，那么生成的python文件即具有相同绘图能力的turtle代码。\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from tkinter import *\n",
    "from tkinter.ttk import *\n",
    "from tkinter import messagebox\n",
    "try:\n",
    "    from PIL import Image, ImageTk\n",
    "except ImportError:\n",
    "    import pip\n",
    "    pip.main(['install', 'pillow'])\n",
    "    from PIL import Image, ImageTk\n",
    "try:\n",
    "    import windnd\n",
    "except ImportError:\n",
    "    import pip\n",
    "    pip.main(['install', 'windnd'])\n",
    "    import windnd\n",
    "import itertools\n",
    "import turtle\n",
    "import os,time\n",
    "import math\n",
    "import random\n",
    "\n",
    "class _Picture:\n",
    "    def __init__(self, path,size):\n",
    "        self.path = path\n",
    "        self.target_size=size\n",
    "        self.image = Image.open(path).convert('RGBA')\n",
    "        self.width = self.image.width\n",
    "        self.height = self.image.height\n",
    "        self.sizeChange()\n",
    "        self.getDeviation()\n",
    "        self.origin_tkImage = ImageTk.PhotoImage(self.image)\n",
    "        self.tkImage = ImageTk.PhotoImage(self.image)\n",
    "        self.modifiedImg = self.image.copy()\n",
    "        \n",
    "    def getModifiedData(self):\n",
    "        return self.modifiedImg.load()\n",
    "        \n",
    "    def getTkImage(self,_img):\n",
    "        return ImageTk.PhotoImage(_img)\n",
    "    \n",
    "    def sizeChange(self):\n",
    "        if self.width>self.height:\n",
    "            self.image = self.image.resize((self.target_size[0],int(self.height*self.target_size[0]/self.width)),Image.Resampling.LANCZOS)\n",
    "        else: \n",
    "            self.image = self.image.resize((int(self.width*self.target_size[1]/self.height),self.target_size[1]),Image.Resampling.LANCZOS)\n",
    "        self.width = self.image.width\n",
    "        self.height = self.image.height\n",
    "\n",
    "    def getDeviation(self):\n",
    "        self.width_deviation = (self.target_size[0]-self.width)//2\n",
    "        self.height_deviation = (self.target_size[1]-self.height)//2\n",
    "\n",
    "    def colorLayered(self,span,deviation):\n",
    "        imagedata=self.image.load()\n",
    "        spanGroup=self.getSpanGroup(span,deviation)\n",
    "        _seq=[]\n",
    "        for y in range(self.height):\n",
    "            for x in range(self.width):\n",
    "                _r, _g, _b, _T = imagedata[x, y]\n",
    "                _seq.append((self.getClosestSpan(spanGroup,_r),self.getClosestSpan(spanGroup,_g),self.getClosestSpan(spanGroup,_b),255))\n",
    "        self.modifiedImg=Image.new('RGBA',(self.width,self.height))\n",
    "        self.modifiedImg.putdata(_seq)\n",
    "        self.tkImage = self.getTkImage(self.modifiedImg)\n",
    "        return len(set(_seq))\n",
    "                \n",
    "    def getClosestSpan(self,spanGroup,num):\n",
    "        assert num>=0 and num<=255, 'num must be in range(0,256)'\n",
    "        for i in range(len(spanGroup)):\n",
    "            if num>=spanGroup[i][0] and num<=spanGroup[i][1]:\n",
    "                if spanGroup[i][0]-num>=num-spanGroup[i][1]:\n",
    "                    return spanGroup[i][0]\n",
    "                else:\n",
    "                    return spanGroup[i][1]\n",
    "        \n",
    "    def getSpanGroup(self,span,deviation):\n",
    "        assert span>=1 and span<=255, 'span must be in range(0,256)'\n",
    "        assert deviation>=0 and deviation<=255, 'deviation must be in range(0,256)'\n",
    "        spanGroup=[]\n",
    "        if deviation!=0:\n",
    "            spanGroup.append((0,deviation))\n",
    "            deviation+=1\n",
    "        for i in range(0+deviation,256+2*span+deviation,span):\n",
    "            if i<=255:\n",
    "                if i+span-1<=255:\n",
    "                    spanGroup.append((i,i+span-1))\n",
    "                else:\n",
    "                    spanGroup.append((i,255))\n",
    "            else:\n",
    "                break\n",
    "        return spanGroup\n",
    "\n",
    "class Tool:\n",
    "    @staticmethod\n",
    "    def colorTrans(color):\n",
    "        rgb='#'+hex(color[0])[2:].zfill(2).upper()+hex(color[1])[2:].zfill(2).upper()+hex(color[2])[2:].zfill(2).upper()\n",
    "        return rgb\n",
    "        \n",
    "class Outline:\n",
    "    def __init__(self,pos):\n",
    "        self.lines=[pos]\n",
    "        self.connectArea=[]\n",
    "        self.posArea(pos)\n",
    "        self.dead=False\n",
    "    def __iter__(self):\n",
    "        return iter(self.lines)\n",
    "    def __getitem__(self,index):\n",
    "        return self.lines[index]\n",
    "    def __len__(self):\n",
    "        return len(self.lines)\n",
    "    def __str__(self):\n",
    "        return str(self.lines)\n",
    "    def __repr__(self):\n",
    "        return str(self.lines)\n",
    "    def die(self):\n",
    "        self.dead=True\n",
    "    def posArea(self,pos):\n",
    "        self.connectArea.extend([(pos[0],pos[1]-1),(pos[0],pos[1]+1),(pos[0]-1,pos[1]),(pos[0]+1,pos[1]),(pos[0]-1,pos[1]-1),(pos[0]-1,pos[1]+1),(pos[0]+1,pos[1]-1),(pos[0]+1,pos[1]+1)])\n",
    "    def add(self,pos):\n",
    "        self.lines.append(pos)\n",
    "        self.posArea(pos)\n",
    "    def addline(self,line):\n",
    "        for p in line:\n",
    "            self.add(p)\n",
    "    def getConnectArea(self):\n",
    "        return set(self.connectArea)\n",
    "    def isConnect(self,pos):\n",
    "        return pos in self.getConnectArea()\n",
    "    def lineSet(self):\n",
    "        return set(self.lines)\n",
    "    def connectCompare(self,outline):\n",
    "        if id(self)==id(outline):\n",
    "            return 0\n",
    "        if outline.dead:\n",
    "            return 0\n",
    "        return len(self.getConnectArea()&outline.lineSet())\n",
    "            \n",
    "            \n",
    "class PixelGroup:\n",
    "    def __init__(self,pos,color,boundary):\n",
    "        self.PixelBox=[pos] #实际像素坐标\n",
    "        self.decareBox={pos[0]:{pos[1]}}   #实际像素坐标区域\n",
    "        self.color=color\n",
    "        self.rgb=Tool.colorTrans(color)\n",
    "        self.level=1    #层级\n",
    "        self.boundary=boundary\n",
    "        self.virtualArea={} #虚拟像素坐标区域集合\n",
    "        self.virtualSize=0  #虚拟像素坐标区域大小\n",
    "        self.AreaBox_X={}\n",
    "        self.AreaBox_Y={}\n",
    "        self.outline=[] #轮廓\n",
    "        #=================#\n",
    "        self.grandFather=[]\n",
    "        self.father=[]\n",
    "        self.son=[]\n",
    "        self.grandSon=[]\n",
    "        self.fellow=[]\n",
    "        \n",
    "    def add(self,pos):\n",
    "        self.PixelBox.append(pos)\n",
    "        \n",
    "    def isGroup(self,color,near=1):\n",
    "        if self.color[0] in range(color[0]-near,color[0]+near+1) and self.color[1] in range(color[1]-near,color[1]+near+1) and self.color[2] in range(color[2]-near,color[2]+near+1):\n",
    "            return True\n",
    "        return False\n",
    "    \n",
    "    def inGroup(self,pos):\n",
    "        return pos in self.PixelBox\n",
    "    \n",
    "    def sameSeniority(self,group):\n",
    "        self.fellow.append(group)\n",
    "    \n",
    "    def _elementMapping(self):\n",
    "        self.mapping=[]\n",
    "        for i in self.PixelBox:\n",
    "            self.mapping.append(hash(i))\n",
    "    \n",
    "    def getOutline(self):\n",
    "        #获得了轮廓，但未分出外轮廓\n",
    "        self._Boundary=self._getMaxBoundary()\n",
    "        _outlineX=[]\n",
    "        _outlineY=[]\n",
    "        for x in range(self._Boundary[0],self._Boundary[1]+1):\n",
    "            for y in range(self._Boundary[2],self._Boundary[3]+1):\n",
    "                if x in self.AreaBox_Y[y] and y in self.AreaBox_X[x]:\n",
    "                    if x-1 not in self.AreaBox_Y[y] or x+1 not in self.AreaBox_Y[y]:\n",
    "                        _outlineX.append((x,y))\n",
    "        for x in range(self._Boundary[0],self._Boundary[1]+1):\n",
    "            for y in range(self._Boundary[2],self._Boundary[3]+1):\n",
    "                if x in self.AreaBox_Y[y] and y in self.AreaBox_X[x]:\n",
    "                    if y-1 not in self.AreaBox_X[x] or y+1 not in self.AreaBox_X[x]:\n",
    "                        _outlineY.append((x,y))\n",
    "        self.outline=list(set(_outlineX)|set(_outlineY))\n",
    "    \n",
    "    def outerOutlineStrip(self):\n",
    "        #去除内轮廓\n",
    "        if len(self.outline)<=12:\n",
    "            return\n",
    "        _outlineGroup=[]\n",
    "        for pos in self.outline:\n",
    "            if _outlineGroup==[]:\n",
    "                _outlineGroup.append(Outline(pos))\n",
    "                continue\n",
    "            _addlines=None\n",
    "            _deleteline=[]\n",
    "            for outlines in _outlineGroup:\n",
    "                if _addlines==None:\n",
    "                    if outlines.isConnect(pos):\n",
    "                        outlines.add(pos)\n",
    "                        _addlines=outlines\n",
    "                        continue\n",
    "                else:\n",
    "                    if outlines.isConnect(pos):\n",
    "                        _addlines.addline(outlines)\n",
    "                        _deleteline.append(outlines)\n",
    "            else:\n",
    "                _outlineGroup.append(Outline(pos))\n",
    "                             \n",
    "            for lines in _deleteline:\n",
    "                _outlineGroup.remove(lines)\n",
    "                \n",
    "        _outlineGroup.sort(key=lambda x:len(x),reverse=True)\n",
    "        _delete=[]\n",
    "        for outlines1,outlines2 in itertools.combinations(_outlineGroup,2):\n",
    "            if outlines1.connectCompare(outlines2):\n",
    "                outlines1.addline(outlines2)\n",
    "                outlines2.die()\n",
    "                _delete.append(outlines2)\n",
    "        for outlines in _delete:\n",
    "            _outlineGroup.remove(outlines)\n",
    "            \n",
    "        max_min_list=self._getOutlineMaxMin(_outlineGroup)\n",
    "        \n",
    "        for lines in _outlineGroup:\n",
    "            if max_min_list[0] in [i[0] for i in lines] and max_min_list[1] in [i[0] for i in lines] and max_min_list[2] in [i[1] for i in lines] and max_min_list[3] in [i[1] for i in lines]:\n",
    "                self.outline=list(lines)\n",
    "                break\n",
    "        self.outline=list(set(self.outline))\n",
    "            \n",
    "    def _isConnect(self,line1,line2):\n",
    "        for p in line1:\n",
    "            if self._posNear(p,line2):\n",
    "                return True\n",
    "        return False\n",
    "            \n",
    "        \n",
    "    def _getOutlineMaxMin(self,lines):\n",
    "        _All_X=[]\n",
    "        _All_Y=[]\n",
    "        for line in lines:\n",
    "            for p in line:\n",
    "                _All_X.append(p[0])\n",
    "                _All_Y.append(p[1])\n",
    "        return (min(_All_X),max(_All_X),min(_All_Y),max(_All_Y))\n",
    "        \n",
    "        \n",
    "    def _posNear(self,pos,line):\n",
    "        Octopus=((pos[0]+1,pos[1]),(pos[0]+1,pos[1]+1),(pos[0],pos[1]+1),(pos[0]-1,pos[1]+1),(pos[0]-1,pos[1]),(pos[0]-1,pos[1]-1),(pos[0],pos[1]-1),(pos[0]+1,pos[1]-1))\n",
    "        \n",
    "        for p in Octopus:\n",
    "            if p in line:\n",
    "                return True\n",
    "        return False\n",
    "        \n",
    "    def lowerSeniority(self,group):\n",
    "        self.levelUp()\n",
    "        if self.father:\n",
    "            if group.level<self.father[0].level:\n",
    "                self.grandFather.append(self.father[0])\n",
    "                self.father=[group]\n",
    "            else:\n",
    "                self.grandFather.append(group)\n",
    "        else:\n",
    "            self.father.append(group)\n",
    "        for i in self.son:\n",
    "            i.lowerSeniority(group)\n",
    "        \n",
    "    def higherSeniority(self,group):\n",
    "        for i in self.son:\n",
    "            if group in i.father:\n",
    "                self.grandSon.append(i)\n",
    "                self.son.remove(i)\n",
    "                group.higherSeniority(i)\n",
    "            elif group in i.son:\n",
    "                self.grandSon.append(group)\n",
    "            else:\n",
    "                self.son.append(group)\n",
    "\n",
    "    def levelUp(self):\n",
    "        self.level+=1\n",
    "    \n",
    "    def relationship(self,group):\n",
    "        if group in self.father or group in self.son or group in self.fellow or group in self.grandFather or group in self.grandSon:\n",
    "            return True\n",
    "        else:\n",
    "            return False\n",
    "            \n",
    "    def initAreaBox(self):\n",
    "        for p in self.PixelBox:\n",
    "            if p[0] in self.AreaBox_X:\n",
    "                self.AreaBox_X[p[0]][p[1]]=1\n",
    "            else:\n",
    "                self.AreaBox_X[p[0]]={p[1]:1}\n",
    "            if p[1] in self.AreaBox_Y:\n",
    "                self.AreaBox_Y[p[1]][p[0]]=1\n",
    "            else:\n",
    "                self.AreaBox_Y[p[1]]={p[0]:1}\n",
    "    \n",
    "    def _getMaxBoundary(self):\n",
    "        _x=[]\n",
    "        _y=[]\n",
    "        for i in self.PixelBox:\n",
    "            _x.append(i[0])\n",
    "            _y.append(i[1])\n",
    "        return (min(_x),max(_x),min(_y),max(_y))\n",
    "\n",
    "    def getStartPos(self):\n",
    "        _x=min(self.AreaBox_X.keys())\n",
    "        _y=min(self.AreaBox_X[_x].keys())\n",
    "        return (_x,_y)\n",
    "        \n",
    "    def isBoundary(self,pos):\n",
    "        if pos[0] == max(self.AreaBox_Y[pos[1]]) or pos[0] == min(self.AreaBox_Y[pos[1]]):\n",
    "            return 1\n",
    "        if pos[1] == max(self.AreaBox_X[pos[0]]) or pos[1] == min(self.AreaBox_X[pos[0]]):\n",
    "            return 1\n",
    "        return 0\n",
    "        \n",
    "        \n",
    "    def getVirtualArea(self):\n",
    "        _virtualAreaX=[]\n",
    "        _virtualAreaY=[]\n",
    "        for y in range(self._Boundary[2],self._Boundary[3]+1):\n",
    "            for x in range(self._Boundary[0],self._Boundary[1]+1):\n",
    "                if x>=min(self.AreaBox_Y[y]) and x<=max(self.AreaBox_Y[y]):\n",
    "                    _virtualAreaX.append((x,y))\n",
    "        for x in range(self._Boundary[0],self._Boundary[1]+1):\n",
    "            for y in range(self._Boundary[2],self._Boundary[3]+1):\n",
    "                if y>=min(self.AreaBox_X[x]) and y<=max(self.AreaBox_X[x]):\n",
    "                    _virtualAreaY.append((x,y))\n",
    "        self.virtualArea=set(_virtualAreaX)&set(_virtualAreaY)\n",
    "        self.virtualSize=len(self.virtualArea)\n",
    "        \n",
    "            \n",
    "class PixelManager:\n",
    "    def __init__(self,width,height,imgdata,root):\n",
    "        self.size_w=width\n",
    "        self.size_h=height\n",
    "        self.imgdata=imgdata\n",
    "        self.GroupList=[]\n",
    "        self.GroupPos={}\n",
    "        self.VirtualPos={}\n",
    "        self.GroupCount=0\n",
    "        self.root=root\n",
    "    def shuffleGroup(self):\n",
    "        random.shuffle(self.GroupList)\n",
    "            \n",
    "    def processWindow(self,pos=None,mid=False):\n",
    "        self.process_W=Toplevel(self.root)\n",
    "        self.process_W.attributes('-topmost',1)\n",
    "        self.process_W.title('Processing')\n",
    "        if mid:\n",
    "                self.process_W.geometry(f'235x85+{self.root.winfo_screenwidth()//2-117}+{self.root.winfo_screenheight()//2-42}')\n",
    "        else:\n",
    "            if pos:\n",
    "                self.process_W.geometry('235x85+{}+{}'.format(pos[0],pos[1]))\n",
    "            else:\n",
    "                self.process_W.geometry('235x85')\n",
    "        self.process_W.resizable(0,0)\n",
    "        self._Label_process=Label(self.process_W,text='Processing...0/0')\n",
    "        self._Label_process.place(anchor=CENTER,relx=0.5,rely=0.5)\n",
    "        \n",
    "    def process(self,step,total_step,current,total,info=None):\n",
    "        if info:\n",
    "            self._Label_process.config(text='Processing...({}/{})\\n{}/{}\\n{}'.format(step,total_step,current,total,info))\n",
    "        else:\n",
    "            self._Label_process.config(text='Processing...({}/{})\\n{}/{}'.format(step,total_step,current,total))\n",
    "        self.process_W.update()\n",
    "        \n",
    "    def timeStatistical(self,timeStr):\n",
    "        self._Label_process.config(text='任务已完成!\\n用时{}'.format(timeStr))\n",
    "        self.process_W.update()\n",
    "        \n",
    "    def processEnd(self):\n",
    "        self.process_W.destroy()\n",
    "        \n",
    "    def getMaxLevel(self):\n",
    "        _maxLevel=0\n",
    "        for g in self.GroupList:\n",
    "            if g.level>_maxLevel:\n",
    "                _maxLevel=g.level\n",
    "        return _maxLevel\n",
    "        \n",
    "    def groupGetOutline(self):\n",
    "        change_count=1\n",
    "        current_count=1\n",
    "        self.process(2,4,round(current_count/self.GroupCount*100,2),'100%','确定像素组轮廓')\n",
    "        for g in self.GroupList:\n",
    "            if change_count==10:\n",
    "                self.process(2,4,round(current_count/self.GroupCount*100,2),'100%','确定像素组轮廓')\n",
    "                change_count=0\n",
    "            change_count+=1\n",
    "            current_count+=1\n",
    "            g.initAreaBox()\n",
    "            g.getOutline()\n",
    "            g.outerOutlineStrip()\n",
    "        \n",
    "    def grouping(self):\n",
    "        self.processWindow()\n",
    "        for y in range(self.size_h):\n",
    "            for x in range(self.size_w):\n",
    "                if x>=1:\n",
    "                    if self.GroupPos[x-1,y].isGroup(self.imgdata[x,y]):\n",
    "                        self.GroupPos[x-1,y].add((x,y))\n",
    "                        self.GroupPos[x,y]=self.GroupPos[x-1,y]\n",
    "                        continue\n",
    "                if y>=1:\n",
    "                    if self.GroupPos[x,y-1].isGroup(self.imgdata[x,y]):\n",
    "                        self.GroupPos[x,y-1].add((x,y))\n",
    "                        self.GroupPos[x,y]=self.GroupPos[x,y-1]\n",
    "                        continue\n",
    "                # if x>=1 and y>=1:\n",
    "                #     if self.GroupPos[x-1,y-1].isGroup(self.imgdata[x,y]):\n",
    "                #         self.GroupPos[x-1,y-1].add((x,y))\n",
    "                #         self.GroupPos[x,y]=self.GroupPos[x-1,y-1]\n",
    "                #         continue\n",
    "                # if x<=self.size_w-2 and y>=1:\n",
    "                #     if self.GroupPos[x+1,y-1].isGroup(self.imgdata[x,y]):\n",
    "                #         self.GroupPos[x+1,y-1].add((x,y))\n",
    "                #         self.GroupPos[x,y]=self.GroupPos[x+1,y-1]\n",
    "                #         continue\n",
    "                    \n",
    "                self.GroupList.append(PixelGroup((x,y),self.imgdata[x,y],(self.size_w,self.size_h)))\n",
    "                self.GroupPos[x,y]=self.GroupList[-1]\n",
    "        _tmpdel=[]\n",
    "        current_count=1\n",
    "        total_count=self.size_w*self.size_h\n",
    "        change_count=1\n",
    "        self.process(1,4,round(current_count/total_count*100,2),'100%','整理像素组')\n",
    "        for y in range(self.size_h-1,-1,-1):\n",
    "            for x in range(self.size_w-1,-1,-1):\n",
    "                change_count+=1\n",
    "                if change_count==1000:\n",
    "                    self.process(1,4,round(current_count/total_count*100,2),'100%','整理像素组')\n",
    "                    change_count=0\n",
    "                if x>=1:\n",
    "                    if self.GroupPos[x-1,y] != self.GroupPos[x,y]:\n",
    "                        if self.GroupPos[x-1,y].isGroup(self.GroupPos[x,y].color):\n",
    "                            _tmpdel.append(self.GroupPos[x-1,y])\n",
    "                            self.groupMerge(self.GroupPos[x-1,y],self.GroupPos[x,y])\n",
    "                            continue\n",
    "                if y>=1:\n",
    "                    if self.GroupPos[x,y-1] != self.GroupPos[x,y]:\n",
    "                        if self.GroupPos[x,y-1].isGroup(self.GroupPos[x,y].color):\n",
    "                            _tmpdel.append(self.GroupPos[x,y-1])\n",
    "                            self.groupMerge(self.GroupPos[x,y-1],self.GroupPos[x,y])\n",
    "                            continue\n",
    "                # if x>=1 and y>=1:\n",
    "                #     if self.GroupPos[x-1,y-1] != self.GroupPos[x,y]:\n",
    "                #         if self.GroupPos[x-1,y-1].isGroup(self.GroupPos[x,y].color):\n",
    "                #             _tmpdel.append(self.GroupPos[x-1,y-1])\n",
    "                #             self.groupMerge(self.GroupPos[x-1,y-1],self.GroupPos[x,y])\n",
    "                #             continue\n",
    "                # if x<=self.size_w-2 and y>=1:\n",
    "                #     if self.GroupPos[x+1,y-1] != self.GroupPos[x,y]:\n",
    "                #         if self.GroupPos[x+1,y-1].isGroup(self.GroupPos[x,y].color):\n",
    "                #             _tmpdel.append(self.GroupPos[x+1,y-1])\n",
    "                #             self.groupMerge(self.GroupPos[x+1,y-1],self.GroupPos[x,y])\n",
    "                #             continue\n",
    "                current_count+=1\n",
    "        \n",
    "        _tmpdel=list(set(_tmpdel))\n",
    "        for g in _tmpdel:\n",
    "            self.GroupList.remove(g)\n",
    "                \n",
    "        self.GroupCount=len(self.GroupList)\n",
    "        \n",
    "    def groupMerge(self,group1,group2):\n",
    "        _tmp=[]\n",
    "        \n",
    "        for p in self.GroupPos:\n",
    "            if self.GroupPos[p]==group1:\n",
    "                _tmp.append(p)\n",
    "        for p in _tmp:\n",
    "            self.GroupPos[p]=group2\n",
    "        for _d in group1.PixelBox:\n",
    "            group2.add(_d)\n",
    "\n",
    "    def groupLevelJudge(self):\n",
    "        change_count=1\n",
    "        current_count=1\n",
    "        self.process(3,4,round(current_count/self.GroupCount*100,2),'100%','像素组分级准备')\n",
    "        for g in self.GroupList:\n",
    "            if change_count==10:\n",
    "                self.process(3,4,round(current_count/self.GroupCount*100,2),'100%','像素组分级准备')\n",
    "                change_count=0\n",
    "            change_count+=1\n",
    "            current_count+=1\n",
    "            g.getVirtualArea()\n",
    "            for p in g.virtualArea:\n",
    "                if p not in self.VirtualPos:\n",
    "                    self.VirtualPos[p]=[g]\n",
    "                else:\n",
    "                    self.VirtualPos[p].append(g)\n",
    "        change_count=1\n",
    "        current_count=1\n",
    "        total_count=len(self.VirtualPos)\n",
    "        self.process(4,4,round(current_count/total_count*100,2),'100%','像素组对比评级')\n",
    "        for v in self.VirtualPos:\n",
    "            if change_count==1000:\n",
    "                self.process(4,4,round(current_count/total_count*100,2),'100%','像素组对比评级')\n",
    "                change_count=0\n",
    "            change_count+=1\n",
    "            current_count+=1\n",
    "            if len(self.VirtualPos[v])>1:\n",
    "                self.quickcompare(self.VirtualPos[v])\n",
    "        \n",
    "        self.processEnd()\n",
    "        \n",
    "        # self.tstlevel=Toplevel()\n",
    "        # self.tstlevel.title('测试')\n",
    "        # self.tstlevel.geometry('800x800')\n",
    "        # self.tstlevel.resizable(0,0)\n",
    "        # tstBoard=Canvas(self.tstlevel,width=800,height=800,bg='white')\n",
    "        # tstBoard.pack()\n",
    "\n",
    "        # n=0\n",
    "        # for g in self.GroupList:\n",
    "        #     n+=1\n",
    "        #     if n in [3]:\n",
    "        #     # if g.level==curLevel:\n",
    "        #         # print(len(g.PixelBox))\n",
    "        #         # for p in g.PixelBox:\n",
    "        #         #     tstBoard.create_line(p[0],p[1],p[0]+1,p[1]+1,fill=g.rgb)\n",
    "        #         # print(len(g.outline))\n",
    "        #         print(len(set(g.outline)))\n",
    "        #         print(g.rgb)\n",
    "        #         g.rgb='#FF0000'\n",
    "        #         print(g.outline)\n",
    "        #         for p in g.outline:\n",
    "        #             tstBoard.create_line(p[0],p[1],p[0]+1,p[1]+1,fill='red')\n",
    "        #     else:\n",
    "        #         for p in g.PixelBox:\n",
    "        #             tstBoard.create_line(p[0],p[1],p[0]+1,p[1]+1,fill=g.rgb)\n",
    "\n",
    "                \n",
    "    def quickcompare(self,_grouplist):\n",
    "        if len(_grouplist)==2:\n",
    "            if _grouplist[0].relationship(_grouplist[1]) or _grouplist[1].relationship(_grouplist[0]):\n",
    "                return\n",
    "            _result=self.compare(_grouplist[0],_grouplist[1])\n",
    "            if _result:\n",
    "                _result[0].higherSeniority(_result[1])\n",
    "                _result[1].lowerSeniority(_result[0])\n",
    "            else:\n",
    "                _grouplist[0].sameSeniority(_grouplist[1])\n",
    "                _grouplist[1].sameSeniority(_grouplist[0])\n",
    "        else:\n",
    "            for _couple in itertools.combinations(_grouplist,2):\n",
    "                if _couple[0].relationship(_couple[1]) or _couple[1].relationship(_couple[0]):\n",
    "                    continue\n",
    "                _result=self.compare(_couple[0],_couple[1])\n",
    "                if _result:\n",
    "                    _result[0].higherSeniority(_result[1])\n",
    "                    _result[1].lowerSeniority(_result[0])\n",
    "                else:\n",
    "                    _couple[0].sameSeniority(_couple[1])\n",
    "                    _couple[1].sameSeniority(_couple[0])\n",
    "                \n",
    "    def compare(self,g1,g2):\n",
    "        if g1.virtualSize>g2.virtualSize:\n",
    "            groupBig=g1\n",
    "            groupSmall=g2\n",
    "        elif g1.virtualSize<g2.virtualSize:\n",
    "            groupBig=g2\n",
    "            groupSmall=g1\n",
    "        else:\n",
    "            return None\n",
    "        if groupBig.virtualSize>groupSmall.virtualSize:\n",
    "            if len(groupSmall.virtualArea-groupBig.virtualArea)==0:\n",
    "                _boundarycount=0\n",
    "                for p in groupSmall.virtualArea:\n",
    "                    _boundarycount+=groupBig.isBoundary(p)\n",
    "                if _boundarycount==0:\n",
    "                    return (groupBig,groupSmall)\n",
    "        return None\n",
    "            \n",
    "class AutoTurtleGenerator:\n",
    "    def __init__(self):\n",
    "        self.Picture=None\n",
    "        self.imgFile=None\n",
    "        self.span=10    #色彩分辨率 10-255\n",
    "        self.deviation=0    #色彩偏移 0-255\n",
    "        self.colorNum=0\n",
    "        self.RunFlag=False\n",
    "        self.Manager=None\n",
    "        self.turtleHandle=None\n",
    "        \n",
    "    def main(self):\n",
    "        self.buildWindow()\n",
    "        \n",
    "    def dropFile(self,file):\n",
    "        if not self.RunFlag:\n",
    "            self.imgFile=file[-1].decode('gb2312')\n",
    "            if self.imgFile.split('.')[-1] not in ['png','jpg','jpeg','bmp']:\n",
    "                self.errorShow('Only support png,jpg,jpeg,bmp')\n",
    "                return\n",
    "            if self.Picture!=None:\n",
    "                self.Picture.image.close()\n",
    "                self._Canvas_origin.delete('textTags')\n",
    "                self._Canvas_origin.delete('originPic')\n",
    "                self._Canvas_Modified.delete('modifiedPic')\n",
    "            try:\n",
    "                self.Picture=_Picture(self.imgFile,(400,400))\n",
    "            except:\n",
    "                self.errorShow('Open file error')\n",
    "                return\n",
    "            self._Canvas_origin.create_image(2+self.Picture.width_deviation,2+self.Picture.height_deviation,anchor=NW,image=self.Picture.origin_tkImage,tag='originPic')\n",
    "            self.colorNum=self.Picture.colorLayered(self.span,self.deviation)\n",
    "            self._Frame_Control['text']='调整 色彩数量: '+str(self.colorNum)\n",
    "            self._Canvas_Modified.create_image(2+self.Picture.width_deviation,2+self.Picture.height_deviation,anchor=NW,image=self.Picture.tkImage,tag='modifiedPic')\n",
    "            self.ButtonABLE()\n",
    "        \n",
    "    def errorShow(self,info):\n",
    "        messagebox.showerror('Error',info)\n",
    "        \n",
    "    def spanChange(self,event):\n",
    "        self.span=int(float(event))\n",
    "        if self._Scale_span.get()!=self.span:\n",
    "            self._Scale_span.set(self.span)\n",
    "        self._Label_span['text']='色彩分辨率: '+str(self.span)\n",
    "            \n",
    "    def spanChangeTo(self,event):\n",
    "        if self.Picture:\n",
    "            self.colorNum=self.Picture.colorLayered(self.span,self.deviation)\n",
    "            self._Frame_Control['text']='调整 色彩数量: '+str(self.colorNum)\n",
    "            self._Canvas_Modified.delete('modifiedPic')\n",
    "            self._Canvas_Modified.create_image(2+self.Picture.width_deviation,2+self.Picture.height_deviation,anchor=NW,image=self.Picture.tkImage,tag='modifiedPic')\n",
    "        \n",
    "    def deviationChange(self,event):\n",
    "        self.deviation=int(float(event))\n",
    "        if self._Scale_deviation.get()!=self.deviation:\n",
    "            self._Scale_deviation.set(self.deviation)\n",
    "        self._Label_deviation['text']='色彩偏移: '+str(self.deviation)\n",
    "    \n",
    "    def deviationChangeTo(self,event):\n",
    "        if self.Picture:\n",
    "            self.colorNum=self.Picture.colorLayered(self.span,self.deviation)\n",
    "            self._Frame_Control['text']='调整 色彩数量: '+str(self.colorNum)\n",
    "            self._Canvas_Modified.delete('modifiedPic')\n",
    "            self._Canvas_Modified.create_image(2+self.Picture.width_deviation,2+self.Picture.height_deviation,anchor=NW,image=self.Picture.tkImage,tag='modifiedPic')\n",
    "        \n",
    "    def ButtonABLE(self):\n",
    "        if self.Picture:\n",
    "            self._Button_Run['state']=NORMAL\n",
    "        else:\n",
    "            self._Button_Run['state']=DISABLED\n",
    "    \n",
    "    def buildWindow(self):\n",
    "        self.root=Tk()\n",
    "        self.root.title('AutoTurtleGenerator')\n",
    "        self.root.geometry('900x650')\n",
    "        self.root.resizable(0,0)\n",
    "        \n",
    "        self.turtleRecord=TurtleRecord(self.root)\n",
    "        \n",
    "        _Frame_origin=LabelFrame(self.root,text='原始图像',width=430,height=430)\n",
    "        _Frame_origin.place(anchor=CENTER,relx=0.5,rely=0.5,x=-220,y=-100)\n",
    "        self._Canvas_origin=Canvas(_Frame_origin,width=400,height=400,background='black')\n",
    "        self._Canvas_origin.place(anchor=CENTER,relx=0.5,rely=0.5)\n",
    "        self._Canvas_origin.create_text(200,200,text='拖动图片到此处',tag='textTags',fill='white')\n",
    "        windnd.hook_dropfiles(self._Canvas_origin,func=self.dropFile)\n",
    "        _Frame_Modified=LabelFrame(self.root,text='处理后',width=430,height=430)\n",
    "        _Frame_Modified.place(anchor=CENTER,relx=0.5,rely=0.5,x=220,y=-100)\n",
    "        self._Canvas_Modified=Canvas(_Frame_Modified,width=400,height=400,background='black')\n",
    "        self._Canvas_Modified.place(anchor=CENTER,relx=0.5,rely=0.5)\n",
    "        self._Frame_Control=LabelFrame(self.root,text='调整 色彩数量: 0',width=870,height=200)\n",
    "        self._Frame_Control.place(anchor=CENTER,relx=0.5,rely=0.5,x=0,y=215)\n",
    "        \n",
    "        self._Label_span=Label(self._Frame_Control,text='色彩分辨率: '+str(self.span))\n",
    "        self._Label_span.place(anchor=CENTER,relx=0.2,rely=0.3,x=-70)\n",
    "        self._Label_deviation=Label(self._Frame_Control,text='色彩偏移: '+str(self.deviation))\n",
    "        self._Label_deviation.place(anchor=CENTER,relx=0.2,rely=0.6,x=-70)\n",
    "        \n",
    "        self._Scale_span=Scale(self._Frame_Control,from_=10,to=255,orient=HORIZONTAL,length=400,command=self.spanChange)\n",
    "        self._Scale_span.place(anchor=CENTER,relx=0.5,rely=0.3,x=-50)\n",
    "        self._Scale_span.bind('<ButtonRelease-1>',self.spanChangeTo)\n",
    "        self._Scale_span.set(self.span)\n",
    "        self._Scale_deviation=Scale(self._Frame_Control,from_=0,to=255,orient=HORIZONTAL,length=400,command=self.deviationChange)\n",
    "        self._Scale_deviation.place(anchor=CENTER,relx=0.5,rely=0.6,x=-50)\n",
    "        self._Scale_deviation.bind('<ButtonRelease-1>',self.deviationChangeTo)\n",
    "        self._Scale_deviation.set(self.deviation)\n",
    "        \n",
    "        from tkinter import Button as OldButton\n",
    "        self._Button_Run=OldButton(self._Frame_Control,text='开始自动化',width=20,height=5,command=self.run,state=DISABLED,relief='groove')\n",
    "        self._Button_Run.place(anchor=CENTER,relx=0.8,rely=0.5,x=50)\n",
    "        \n",
    "        self._State_randomGroup=BooleanVar()\n",
    "        self._State_randomGroup.set(False)\n",
    "        self._State_quickmode=BooleanVar()\n",
    "        self._State_quickmode.set(False)\n",
    "        self._Checkbutton_randomGroup=Checkbutton(self._Frame_Control,text='随机分组',variable=self._State_randomGroup)\n",
    "        self._Checkbutton_randomGroup.place(anchor=CENTER,relx=0.75,rely=0.9,x=50,y=-10)\n",
    "        self._Checkbutton_quickmode=Checkbutton(self._Frame_Control,text='快速模式',variable=self._State_quickmode)\n",
    "        self._Checkbutton_quickmode.place(anchor=CENTER,relx=0.85,rely=0.9,x=50,y=-10)\n",
    "        self.root.mainloop()\n",
    "\n",
    "    def run(self):\n",
    "        self._Scale_span['state']=DISABLED\n",
    "        self._Scale_deviation['state']=DISABLED\n",
    "        self._Button_Run['state']=DISABLED\n",
    "        self._Checkbutton_quickmode['state']=DISABLED\n",
    "        self._Checkbutton_randomGroup['state']=DISABLED\n",
    "        self.Manager=PixelManager(self.Picture.width,self.Picture.height,self.Picture.getModifiedData(),self.root)\n",
    "        self.Manager.grouping()\n",
    "        self.Manager.groupGetOutline()\n",
    "        self.Manager.groupLevelJudge()\n",
    "        self.turtleHandle=TurtleWorks(self.Manager,self.turtleRecord,randomGroup=self._State_randomGroup.get(),quickmode=self._State_quickmode.get())\n",
    "        self.turtleRecord.picAdjustArguments(self.span,self.deviation)\n",
    "        self.turtleHandle.work()\n",
    "        \n",
    "class TurtleRecord:\n",
    "    def __init__(self,root):\n",
    "        self.root=root\n",
    "        self.saveFile=None\n",
    "        self.localPath=os.getcwd()+'\\\\'\n",
    "        self.currentName=1\n",
    "        self.operationList=None\n",
    "    def setOperationList(self,operationList):\n",
    "        self.operationList=operationList\n",
    "    def getNewName(self):\n",
    "        while True:\n",
    "            if os.path.exists(self.localPath+'turtle_'+str(self.currentName)+'.py'):\n",
    "                self.currentName+=1\n",
    "            else:\n",
    "                return 'turtle_'+str(self.currentName)+'.py'\n",
    "        \n",
    "    def picAdjustArguments(self,span,deviation):\n",
    "        self.adjustText='#span='+str(span)+',deviation='+str(deviation)+'\\n'\n",
    "    def setSaveFile(self,saveFile):\n",
    "        self.saveFile=saveFile\n",
    "    def recordWindow(self):\n",
    "        self._record_top=Toplevel(self.root)\n",
    "        self._record_top.title('操作记录')\n",
    "        self._record_top.geometry(f'400x800+{self.root.winfo_screenwidth()-400}+0')\n",
    "        self._record_top.resizable(0,0)\n",
    "        self._Frame_record=LabelFrame(self._record_top,text='输出',width=390,height=790)\n",
    "        self._Frame_record.place(anchor=CENTER,relx=0.5,rely=0.5)\n",
    "        \n",
    "        self._ListBox_record=Listbox(self._Frame_record,width=50,height=42,background='green',foreground='white',selectmode=SINGLE,relief='groove'\n",
    "                                     ,selectbackground='green',selectforeground='white',activestyle='none')\n",
    "        self._ListBox_record.pack(side=LEFT,fill=Y,expand=1)\n",
    "        self._Scrollbar_record=Scrollbar(self._Frame_record)\n",
    "        self._Scrollbar_record.pack(side=RIGHT,fill=Y)\n",
    "        self._ListBox_record.config(yscrollcommand=self._Scrollbar_record.set)\n",
    "        self._Scrollbar_record.config(command=self._ListBox_record.yview)\n",
    "        \n",
    "        \n",
    "    def lengthCheck(self):\n",
    "        if len(self.operationList)>=42:\n",
    "            self._Scrollbar_record.pack(side=RIGHT,fill=Y)\n",
    "        else:\n",
    "            self._Scrollbar_record.pack_forget()\n",
    "    \n",
    "    def record(self,operation):\n",
    "        self._ListBox_record.insert(END,operation)\n",
    "        self._ListBox_record.see(END)\n",
    "        self.lengthCheck()\n",
    "\n",
    "    def addOperation(self,operation):\n",
    "        self.operationList.append(operation)\n",
    "        self.record(operation)\n",
    "        \n",
    "    def addOper(self,operation):\n",
    "        self.addOperation(operation)\n",
    "    \n",
    "    def save(self):\n",
    "        assert self.saveFile!=None,'未设置保存文件'\n",
    "        with open(self.saveFile,'w') as f:\n",
    "            f.write(self.adjustText+'import turtle\\nimport math\\n\\n')\n",
    "            for i in self.operationList:\n",
    "                f.write(i+'\\n')\n",
    "                \n",
    "    def bevelCalc(self,step):\n",
    "        return (step)*math.sqrt(2)\n",
    "    \n",
    "    #=====================以下为turtle操作映射=====================#\n",
    "    def setup(self,*args):\n",
    "        _arg=','.join([str(i) for i in args])\n",
    "        self.addOperation('turtle.setup('+_arg+')')\n",
    "        turtle.setup(*args)\n",
    "    def bgcolor(self,*args):\n",
    "        _arg=','.join([str(i) for i in args])\n",
    "        self.addOperation('turtle.bgcolor(\"'+_arg+'\")')\n",
    "        turtle.bgcolor(*args)\n",
    "    def bevelset(self,*args):\n",
    "        self.addOperation('bevel=math.sqrt(2)')\n",
    "    def pencolor(self,*args):\n",
    "        _arg=','.join([str(i) for i in args])\n",
    "        self.addOperation('turtle.pencolor(\"'+_arg+'\")')\n",
    "        turtle.pencolor(*args)\n",
    "    def pensize(self,*args):    \n",
    "        _arg=','.join([str(i) for i in args])\n",
    "        self.addOperation('turtle.pensize('+_arg+')')\n",
    "        turtle.pensize(*args)\n",
    "    def speed(self,*args):\n",
    "        _arg=','.join([str(i) for i in args])\n",
    "        self.addOperation('turtle.speed('+_arg+')')\n",
    "        turtle.speed(*args)\n",
    "    def pendown(self):\n",
    "        self.addOperation('turtle.pendown()')\n",
    "        turtle.pendown()\n",
    "    def penup(self):\n",
    "        self.addOperation('turtle.penup()')\n",
    "        turtle.penup()\n",
    "    def goto(self,*args):\n",
    "        _arg=','.join([str(i) for i in args])\n",
    "        self.addOperation('turtle.goto('+_arg+')')\n",
    "        turtle.goto(*args)\n",
    "    def fillcolor(self,*args):\n",
    "        _arg=','.join([str(i) for i in args])\n",
    "        self.addOperation('turtle.fillcolor(\"'+_arg+'\")')\n",
    "        turtle.fillcolor(*args)\n",
    "    def begin_fill(self):\n",
    "        self.addOperation('turtle.begin_fill()')\n",
    "        turtle.begin_fill()\n",
    "    def end_fill(self):\n",
    "        self.addOperation('turtle.end_fill()')\n",
    "        turtle.end_fill()\n",
    "    def forward(self,*args):\n",
    "        _arg=','.join([str(i) for i in args])\n",
    "        self.addOperation('turtle.forward('+_arg+')')\n",
    "        turtle.forward(*args)\n",
    "    def forward_bevel(self,*args):\n",
    "        _arg=','.join([str(i) for i in args])\n",
    "        self.addOperation('turtle.forward('+_arg+'*bevel)')\n",
    "        turtle.forward(self.bevelCalc(*args))\n",
    "    def backward(self,*args):\n",
    "        _arg=','.join([str(i) for i in args])\n",
    "        self.addOperation('turtle.backward('+_arg+')')\n",
    "        turtle.backward(*args)\n",
    "    def left(self,*args):\n",
    "        _arg=','.join([str(i) for i in args])\n",
    "        self.addOperation('turtle.left('+_arg+')')\n",
    "        turtle.left(*args)\n",
    "    def right(self,*args):\n",
    "        _arg=','.join([str(i) for i in args])\n",
    "        self.addOperation('turtle.right('+_arg+')')\n",
    "        turtle.right(*args)\n",
    "    def seth(self,*args):\n",
    "        _arg=','.join([str(i) for i in args])\n",
    "        self.addOperation('turtle.seth('+_arg+')')\n",
    "        turtle.seth(*args)\n",
    "    def setheading(self,*args):\n",
    "        _arg=','.join([str(i) for i in args])\n",
    "        self.addOperation('turtle.setheading('+_arg+')')\n",
    "        turtle.setheading(*args)\n",
    "    def dot(self,*args):\n",
    "        _arg=','.join([str(i) for i in args])\n",
    "        self.addOperation('turtle.dot('+_arg+')')\n",
    "        turtle.dot(*args)\n",
    "    def hideturtle(self):\n",
    "        self.addOperation('turtle.hideturtle()')\n",
    "        turtle.hideturtle()\n",
    "    def update(self):\n",
    "        self.addOperation('turtle.update()')\n",
    "        turtle.update()\n",
    "    def done(self):\n",
    "        self.addOperation('turtle.done()')\n",
    "        # turtle.done()\n",
    "            \n",
    "class TurtleWorks:\n",
    "    def __init__(self,GroupManager,record,randomGroup=False,quickmode=False):\n",
    "        self.GroupManager=GroupManager\n",
    "        self.record=record\n",
    "        self.record.setSaveFile(self.record.getNewName())\n",
    "        self.operationList=[]\n",
    "        self.record.setOperationList(self.operationList)\n",
    "        self.workStartTime=time.time()\n",
    "        self.author='Aikko'\n",
    "        self.randomGroup=randomGroup\n",
    "        self.quickmode=quickmode\n",
    "    def initTurtle(self):\n",
    "        self.record.record('import turtle')\n",
    "        self.record.setup(800,800)\n",
    "        self.record.bgcolor('black') \n",
    "        self.record.bevelset()\n",
    "        self.record.pensize(1)\n",
    "        self.record.speed(0)\n",
    "        if self.quickmode:\n",
    "            turtle.tracer(False)\n",
    "    def work(self):\n",
    "        self.record.recordWindow()\n",
    "        self.GroupManager.processWindow()\n",
    "        self.initTurtle()\n",
    "        maxLevel=self.GroupManager.getMaxLevel()\n",
    "        if self.randomGroup:\n",
    "            self.GroupManager.shuffleGroup()\n",
    "        curCount=1\n",
    "        startTime=time.time()\n",
    "        _color=None\n",
    "        for i in range(1,maxLevel+1):\n",
    "            for group in self.GroupManager.GroupList:\n",
    "                if group.level==i:\n",
    "                    self.GroupManager.process(1,1,curCount,self.GroupManager.GroupCount,self.transTime(self.leftTime(startTime,curCount,self.GroupManager.GroupCount)))\n",
    "                    _color=self._work_byGroup(group,_color)\n",
    "                    curCount+=1\n",
    "        self.record.hideturtle()\n",
    "        self.record.done()\n",
    "        turtle.update()\n",
    "        self.record.save()\n",
    "        self.GroupManager.processEnd()\n",
    "        self.GroupManager.processWindow(mid=True)\n",
    "        self.GroupManager.timeStatistical(self.transTime(time.time()-self.workStartTime,True))\n",
    "\n",
    "    def leftTime(self,startTime,curCount,totalCount):\n",
    "        _time=time.time()-startTime\n",
    "        _leftTime=(_time/curCount)*(totalCount-curCount)\n",
    "        return _leftTime\n",
    "    \n",
    "    def transTime(self,seconds,statistical=False):\n",
    "        seconds=int(seconds)\n",
    "        if statistical:\n",
    "            _base_Str=''\n",
    "        else:\n",
    "            _base_Str='预计剩余'\n",
    "        if seconds<60:\n",
    "            return _base_Str+str(seconds)+'秒'\n",
    "        elif seconds<3600:\n",
    "            return _base_Str+str(seconds//60)+'分'+str(seconds%60)+'秒'\n",
    "        else:\n",
    "            return _base_Str+str(seconds//3600)+'小时'+str((seconds%3600)//60)+'分'+str((seconds%3600)%60)+'秒'\n",
    "                \n",
    "    def posDeviation(self,curPos):\n",
    "        '''需增加拉伸补偿'''\n",
    "        return (curPos[0]-200,-curPos[1]+200)\n",
    "    \n",
    "    def _work_byGroup(self,group,_lastColor):\n",
    "        allPixel=group.PixelBox[:]\n",
    "        if len(allPixel)>len(group.outline):\n",
    "            _fillFlag=True\n",
    "        else:\n",
    "            _fillFlag=False\n",
    "        _lastForward=90    #初始值\n",
    "        _step=0\n",
    "        _startPos=group.getStartPos()\n",
    "        _footprint=[_startPos]\n",
    "        self.record.penup()\n",
    "        self.record.goto(*self.posDeviation(_startPos))\n",
    "        if _lastColor!=group.rgb:\n",
    "            self.record.pencolor(group.rgb)\n",
    "        if _fillFlag:\n",
    "            self.record.fillcolor(group.rgb)\n",
    "            self.record.begin_fill()\n",
    "        self.record.pendown()\n",
    "        \n",
    "        if len(allPixel)==1:\n",
    "            self.record.dot(2)\n",
    "            if _fillFlag:\n",
    "                self.record.end_fill()\n",
    "            return\n",
    "        try:\n",
    "            allPixel.remove(_startPos)\n",
    "        except:\n",
    "            pass\n",
    "        _nextPos=_startPos\n",
    "        if len(allPixel)==0:\n",
    "            if _fillFlag:\n",
    "                self.record.end_fill()\n",
    "            return\n",
    "        _,_lastForward=self._groupWork(group,_nextPos,_lastForward)\n",
    "        self.record.seth(_lastForward)\n",
    "        \n",
    "        scattered=len(set(group.outline)-set(_footprint))\n",
    "        _scatteredFlag=False\n",
    "        while True:\n",
    "            _nextPos,_nextForword=self._groupWork(group,_nextPos,_lastForward)\n",
    "            _slopeFlag=False\n",
    "            if _nextForword==_lastForward:\n",
    "                _step+=1\n",
    "            else:\n",
    "                if _lastForward in [0,90,180,270]:\n",
    "                    if _slopeFlag:\n",
    "                        self.record.forward(_step+1)\n",
    "                        _slopeFlag=False\n",
    "                    else:\n",
    "                        self.record.forward(_step)\n",
    "                else:\n",
    "                    self.record.forward_bevel(_step)\n",
    "                    _slopeFlag=True\n",
    "                _step=1\n",
    "                self.record.seth(_nextForword)\n",
    "                _lastForward=_nextForword\n",
    "            try:\n",
    "                allPixel.remove(_startPos)\n",
    "            except:\n",
    "                pass\n",
    "            if _nextPos not in _footprint:\n",
    "                _footprint.append(_nextPos)\n",
    "            if len(set(group.outline)-set(_footprint))==0:\n",
    "                #闭合\n",
    "                if _fillFlag:\n",
    "                    if _startPos==_nextPos:\n",
    "                        _step+=1\n",
    "                        break\n",
    "                else:\n",
    "                    _step+=1\n",
    "                    break\n",
    "            if not _fillFlag:\n",
    "                if len(allPixel)==0:\n",
    "                    if _nextForword==_lastForward:\n",
    "                        _step+=1\n",
    "                    break\n",
    "            if _nextPos==_startPos:\n",
    "                if scattered==len(set(group.outline)-set(_footprint)):\n",
    "                    #主动闭合\n",
    "                    _scatteredFlag=True\n",
    "                    _step+=1\n",
    "                    break\n",
    "                else:\n",
    "                    scattered=len(set(group.outline)-set(_footprint))    \n",
    "                    \n",
    "        if _nextForword in [0,90,180,270]:\n",
    "            if _slopeFlag:\n",
    "                self.record.forward(_step+1)\n",
    "            else:\n",
    "                self.record.forward(_step)\n",
    "        else:\n",
    "            self.record.forward_bevel(_step)\n",
    "        if _fillFlag:\n",
    "            self.record.end_fill()\n",
    "        if _scatteredFlag:\n",
    "            #零散点处理\n",
    "            scattered_point=set(group.outline)-set(_footprint)\n",
    "            for pos in scattered_point:\n",
    "                self.record.penup()\n",
    "                self.record.goto(*self.posDeviation(pos))\n",
    "                self.record.pendown()\n",
    "                self.record.dot(1) \n",
    "        return group.rgb\n",
    "\n",
    "    def bevelCalc(self,step):\n",
    "        return (step)*math.sqrt(2)\n",
    "    \n",
    "    def _groupWork(self,group,curPos,lastForward):\n",
    "        if group.rgb==\"#000000\":\n",
    "            pass\n",
    "        nextPool=[]\n",
    "        if (curPos[0]-1,curPos[1]) in group.outline:\n",
    "            nextPool.append(((curPos[0]-1,curPos[1]),180))\n",
    "        if (curPos[0]-1,curPos[1]-1) in group.outline:\n",
    "            nextPool.append(((curPos[0]-1,curPos[1]-1),135))\n",
    "        if (curPos[0],curPos[1]-1) in group.outline:\n",
    "            nextPool.append(((curPos[0],curPos[1]-1),90))\n",
    "        if (curPos[0]+1,curPos[1]-1) in group.outline:\n",
    "            nextPool.append(((curPos[0]+1,curPos[1]-1),45))\n",
    "        if (curPos[0]+1,curPos[1]) in group.outline:\n",
    "            nextPool.append(((curPos[0]+1,curPos[1]),0))\n",
    "        if (curPos[0]+1,curPos[1]+1) in group.outline:\n",
    "            nextPool.append(((curPos[0]+1,curPos[1]+1),315)) \n",
    "        if (curPos[0],curPos[1]+1) in group.outline:\n",
    "            nextPool.append(((curPos[0],curPos[1]+1),270))\n",
    "        if (curPos[0]-1,curPos[1]+1) in group.outline:\n",
    "            nextPool.append(((curPos[0]-1,curPos[1]+1),225))\n",
    "        nextPos,nextForward=self._getMinAngle(lastForward,nextPool)\n",
    "        return nextPos,nextForward\n",
    "    \n",
    "    def _getMinAngle(self,oldAngle,pool):\n",
    "        assert len(pool)>0,'pool is empty'\n",
    "        minAngle=999\n",
    "        for pos,angle in pool:        \n",
    "            angleDiff=self.__calcAngleDiff(oldAngle,angle)\n",
    "            if angleDiff<minAngle:\n",
    "                minAngle=angleDiff\n",
    "                nextPos=pos\n",
    "                nextAngle=angle\n",
    "        return nextPos,nextAngle\n",
    "\n",
    "    def __calcAngleDiff(self,angle1,angle2):\n",
    "        diff=angle2-angle1\n",
    "        if diff>=0:\n",
    "            if diff-180>=0:\n",
    "                diff-=360\n",
    "        else:\n",
    "            if diff+180<0:\n",
    "                diff+=360\n",
    "        return -diff+180\n",
    "    \n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "if __name__=='__main__':\n",
    "    Auto=AutoTurtleGenerator()\n",
    "    Auto.main()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.10.6 ('python310')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.6"
  },
  "vscode": {
   "interpreter": {
    "hash": "4d9c95c886d4bea4b342123a4d6e8907df0e025cc8ac2fd16e26847e1154d7e7"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
